package org.biodas.jdas.client.threads;

import org.apache.log4j.Logger;
import org.biodas.jdas.client.TypesClient;
import org.biodas.jdas.exceptions.DASClientException;
import org.biodas.jdas.exceptions.ErrorInSource;
import org.biodas.jdas.schema.types.DASTYPES;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observer;

/**
 * Created by IntelliJ IDEA.
 * User: ljgarcia
 * Date: 02/12/11
 * Time: 10:49
 * To change this template use File | Settings | File Templates.
 */
public class TypesClientMultipleSources extends ClientMultipleSources {
    private static Logger logger = Logger.getLogger(TypesClientMultipleSources.class);

    /**
     * Default constructor, it defines an initial pool with 5 threads, a maximum of 10 threads,
     * and a keepAlive time of 5 seconds.
     */
    public TypesClientMultipleSources() {
        super();
    }

    /**
     * Constructor with parameters, it enables to define the initial pool size, maximum pool size,
     * and keep alive time in seconds; it initializes the ThreadPoolExecutor.
     *
     * @param poolSize      Initial pool size
     * @param maxPoolSize   Maximum pool size
     * @param keepAliveTime Keep alive time in seconds
     */
    public TypesClientMultipleSources(int poolSize, int maxPoolSize, long keepAliveTime) {
        super(poolSize, maxPoolSize, keepAliveTime);
    }

    /**
     * Constructor with parameters, it gets an Observer for this object and add it to the observers collection;
     * it also defines an initial pool with 5 threads, a maximum of 10 threads, and a keepAlive time of 5 seconds.
     *
     * @param observer A new observer for this object
     */
    public TypesClientMultipleSources(Observer observer) {
        super(observer);
    }

    /**
     * Constructor with parameters, it enables to define the initial pool size, maximum pool size,
     * and keep alive time in seconds; it initializes the ThreadPoolExecutor.
     *
     * @param poolSize      Initial pool size
     * @param maxPoolSize   Maximum pool size
     * @param keepAliveTime Keep alive time in seconds
     * @param observer      A new observer for this object
     */
    public TypesClientMultipleSources(int poolSize, int maxPoolSize, long keepAliveTime, Observer observer) {
        super(poolSize, maxPoolSize, keepAliveTime, observer);
    }

    /**
     * Fetches data from multiple sources for a list of segments; segments can include ranges.
     * The invoking method is responsible for processing the results.
     * @param sources List of sources (url)
     * @param segments List of segments
     * @param results Map of (source name, (list of segments, DASTYPES responses)) one for each source successfully queried
     * @param errors  Map of (source name, (list of segments, exceptions)) one for each source raising an exception
     */
    public void fetchData(List<String> sources, final List<String> segments, final List<String> types, final Map<String, Map<List<String>, DASTYPES>> results, final Map<String, Map<List<String>, Exception>> errors) {
        final TypesClientMultipleSources self = this;
        for (final String source: sources) {
            this.runTask(new Runnable() {
                @Override
                public void run() {
                    try {
                        TypesClient typesClient = new TypesClient(getConnPropsFor(source));
                        DASTYPES adapter = typesClient.fetchData(source, segments, types);
                        if (results.get(source) == null) {
                            Map<List<String>, DASTYPES> response = new HashMap<List<String>, DASTYPES>();
                            response.put(segments, adapter);
                            results.put(source, response);
                        } else {
                            results.get(source).put(segments, adapter);
                        }
                        logger.debug("Response from " + source + " has been already retrieved");
                        self.changeAndNotify(source);
                    } catch (DASClientException e) {
                        if (errors.get(source) == null) {
                            Map<List<String>, Exception> response = new HashMap<List<String>, Exception>();
                            response.put(segments, e);
                            errors.put(source, response);
                        } else {
                            errors.get(source).put(segments, e);
                        }
                        logger.error("Fetching data from source " + source + " raised an exception: " + e.getMessage());
                        self.changeAndNotify(new ErrorInSource(source, e));
                    }
                }
            });
        }
    }

    /**
     * Fetches data from multiple sources for a list of segments; segments can include ranges.
     * The invoking method is responsible for processing the results.
     * @param sources List of sources (url)
     * @param results Map of (source name, DASTYPES responses) one for each source successfully queried
     * @param errors  Map of (source name, exceptions) one for each source raising an exception
     */
    public void fetchData(List<String> sources, final Map<String, DASTYPES> results, final Map<String, Exception> errors) {
        final TypesClientMultipleSources self = this;
        for (final String source: sources) {
            this.runTask(new Runnable() {
                @Override
                public void run() {
                    try {
                        TypesClient typesClient = new TypesClient(getConnPropsFor(source));
                        DASTYPES adapter = typesClient.fetchData(source);
                        results.put(source, adapter);
                        logger.debug("Response from " + source + " has been already retrieved");
                        self.changeAndNotify(source);
                    } catch (DASClientException e) {
                        errors.put(source, e);
                        logger.error("Fetching data from source " + source + " raised an exception: " + e.getMessage());
                        self.changeAndNotify(new ErrorInSource(source, e));
                    }
                }
            });
        }
    }
}
